#include "framelesswindow.h"
#include <QMouseEvent>
#include <QApplication>
#include <QDebug>

FramelessWindow::FramelessWindow(QWidget *parent) : QWidget(parent)
{
    this->setWindowFlags(windowFlags() | Qt::FramelessWindowHint);    // 去掉标头(标题栏),改为自定义标题栏
    setAttribute(Qt::WA_TranslucentBackground);
    this->installEventFilter(this);
}

void FramelessWindow::setTitleBarWidgets(QVector<QWidget *> widgets)
{
    titleBarWidgets = widgets;
}

void FramelessWindow::mousePressEvent(QMouseEvent *event)
{
    if(event->button() != Qt::LeftButton)  return;
    leftPressed = true;
    wndPos = this->pos();
    pressPos = event->globalPos();

    auto pressedWidget = QApplication::widgetAt(event->globalPos());
    qDebug() << pressedWidget << ", pos: " << event->globalPos();
    if(pressedWidget) {
        foreach (auto w, titleBarWidgets) {
            if(pressedWidget == w) {
                // 点击处在标题栏内部
                isPressedInTitle = true;
                break;
            }
        }
    }
}

/// TODO needs opt
void FramelessWindow::mouseReleaseEvent(QMouseEvent *event)
{
    leftPressed = false;
    isPressedInTitle = false;
}

void FramelessWindow::mouseMoveEvent(QMouseEvent *event)
{
    QPoint globalPos = event->globalPos();
    // 1. 左键未按下
    if ( !leftPressed ) {
        // 窗口不是最大化状态，则光标移动到边界时，要变成缩放的形状（窗口处于最大化状态时，就无须改变光标形状了）
        if ( this->windowState().testFlag(Qt::WindowNoState) ) {
            setCursorShape(globalPos);
        }
        return;
    }

    // 2. 左键按下时
    if ( hoverPos != CENTER ) {
        // 2.1 在边界处按下
        QRect rect = this->rect();
        QPoint topLeft = mapToGlobal(rect.topLeft());
        QPoint bottomRight = mapToGlobal(rect.bottomRight());

        QRect rMove(topLeft, bottomRight);

        switch ( hoverPos ) {
            case TOP:
                // 如果不加if判断，则窗口高度达到最小高度后，会被鼠标 "向下推走"
                if ( bottomRight.y() - globalPos.y() > this->minimumHeight() ) {
                    rMove.setY(globalPos.y());
                }
                break;
            case BOTTOM:
                rMove.setHeight(globalPos.y() - topLeft.y());
                break;
            case LEFT:
                // 如果不加if判断，则窗口高度达到最小宽度后，会被鼠标 "向右推走"
                if ( bottomRight.x() - globalPos.x() > this->minimumWidth() ) {
                    rMove.setX(globalPos.x());
                }
                break;
            case RIGHT:
                rMove.setWidth(globalPos.x() - topLeft.x());
                break;
            case TOP_LEFT:
                if ( bottomRight.y() - globalPos.y() > this->minimumHeight() ) {
                    rMove.setY(globalPos.y());
                }
                if ( bottomRight.x() - globalPos.x() > this->minimumWidth() ) {
                    rMove.setX(globalPos.x());
                }
                break;
            case TOP_RIGHT:
                if ( bottomRight.y() - globalPos.y() > this->minimumHeight() ) {
                    rMove.setY(globalPos.y());
                }
                rMove.setWidth(globalPos.x() - topLeft.x());
                break;
            case BOTTOM_LEFT:
                rMove.setHeight(globalPos.y() - topLeft.y());
                if ( bottomRight.x() - globalPos.x() > this->minimumWidth() ) {
                    rMove.setX(globalPos.x());
                }
                break;
            case BOTTOM_RIGHT:
                rMove.setHeight(globalPos.y() - topLeft.y());
                rMove.setWidth(globalPos.x() - topLeft.x());
                break;
            default:
                break;
        }
        this->setGeometry(rMove);
    } else {
        // 2.2 在标题栏内按下
        if ( isPressedInTitle ) {
            if ( this->isMaximized() ) {
                // 窗口最大化时鼠标拖动标题栏需要完成两个操作：
                // a. 窗口恢复
                // b. 鼠标相对窗口的位置不变
                //    相对位置不变指的是：鼠标点击拖动窗口1/4处进行拖动，复原时鼠标依然位于窗口1/4处
                // 达到此效果，仅需更改窗口的位置即可

                // 计算全屏时，鼠标在 x 轴上，相对于屏幕宽度的百分比
                float width_ratio = float(event->globalPos().x()) / float(this->width());
                qDebug() << "ratio=" << width_ratio;
                qDebug() << "before width = " << this->width();  // 1920

                // a. 窗口恢复
                this->setWindowState(Qt::WindowNoState);
                qDebug() << "after width = " << this->width();  // 600

                // b. 鼠标相对窗口的相对位置不变
                // 和双击时一样，默认会回到最大化之前的位置，所以要修改窗口的位置
                int offset = this->width() * width_ratio;  // 当前窗口相对于鼠标位置的偏移

                wndPos.setX(event->globalPos().x() - offset);
                wndPos.setY(0);
            } else {
                this->move(wndPos + (event->globalPos() - pressPos));
            }
        }
    }
}

void FramelessWindow::setAllWidgetMouseTracking(QWidget *widget)
{
    // 启用鼠标追踪功能
    widget->setMouseTracking(true);
    QObjectList list = widget->children();  // typedef QList<QObject*> QObjectList;
    foreach (QObject* obj, list) {
        // qDebug() << obj->metaObject()->className();  // 类型
        // 用于将字符串字面量转换为 QString 类型，性能更加可避免不必要的转化
        if ( obj->metaObject()->className() == QStringLiteral("QWidget") ) {
            QWidget* w = (QWidget*)obj;
            w->setMouseTracking(true);
            setAllWidgetMouseTracking(w);
        }
    }
}


/* 当前窗口绘制时，给窗口中的所有控件设置鼠标追踪。
 * 这样鼠标在移动到边界时，可以追踪得到，从而设置光标为缩放形状
 */
bool FramelessWindow::eventFilter(QObject *target, QEvent *event)
{
    if ( event->type() == QEvent::Paint ) {
        static bool init = false;
        if ( !init ) {
            init = true;
            setAllWidgetMouseTracking(this);
        }
    }

    // 查看源码可知，父类的实现就是 return false，表示让事件接着传递，也就是传递给对应的控件
    return QWidget::eventFilter(target, event);
}

//bool isInRegion(int x, int y, int xlow, int xpadding, int ylow, int ypadding) {
//    return x >= xlow && x <= xlow + xpadding && y >= ylow && y <= ylow + ypadding;
//}

// 设置光标的形状
void FramelessWindow::setCursorShape(const QPoint &point)
{
    QRect rect = this->rect();  // 当前窗口
#if 0
    // topLeft= QPoint(0,0) , bottomRight= QPoint(599,399)
    qDebug() << " topLeft=" << rect.topLeft() << ", bottomRight=" << rect.bottomRight();
#endif
    // 将组件（控件）中的本地坐标系（局部坐标）映射到全局坐标系
    QPoint topLeft = mapToGlobal(rect.topLeft());
    QPoint bottomRight = mapToGlobal(rect.bottomRight());

    int x = point.x();
    int y = point.y();

    /// @todo needs improvment
    if ( x >= topLeft.x() && x <= topLeft.x() + padding && y >= topLeft.y() && y <= topLeft.y() + padding ) {
        // 左上角
        hoverPos = TOP_LEFT;
        this->setCursor(QCursor(Qt::SizeFDiagCursor));
    } else if ( x <= bottomRight.x() && x >= bottomRight.x() - padding && y <= bottomRight.y() && y >= bottomRight.y() - padding ) {
        // 右下角
        hoverPos = BOTTOM_RIGHT;
        this->setCursor(QCursor(Qt::SizeFDiagCursor));
    } else if ( x >= topLeft.x() && x <= topLeft.x() + padding && y <= bottomRight.y() && y >= bottomRight.y() - padding ) {
        // 左下角
        hoverPos = BOTTOM_LEFT;
        this->setCursor(QCursor(Qt::SizeBDiagCursor));
    } else if ( x <= bottomRight.x() && x >= bottomRight.x() - padding && y >= topLeft.y() && y <= topLeft.y() + padding ) {
        // 右上角
        hoverPos = TOP_RIGHT;
        this->setCursor(QCursor(Qt::SizeBDiagCursor));
    } else if ( x >= topLeft.x() && x <= topLeft.x() + padding ) {
        // 左边
        hoverPos = LEFT;
        this->setCursor(QCursor(Qt::SizeHorCursor));
    } else if ( x <= bottomRight.x() && x >= bottomRight.x() - padding ) {
        // 右边
        hoverPos = RIGHT;
        this->setCursor(QCursor(Qt::SizeHorCursor));
    } else if ( y >= topLeft.y() && y <= topLeft.y() + padding ) {
        // 上边
        hoverPos = TOP;
        this->setCursor(QCursor(Qt::SizeVerCursor));
    } else if ( y <= bottomRight.y() && y >= bottomRight.y() - padding ) {
        // 下边
        hoverPos = BOTTOM;
        this->setCursor(QCursor(Qt::SizeVerCursor));
    } else {
        // 中间
        hoverPos = CENTER;
        this->setCursor(QCursor(Qt::ArrowCursor));
    }
}

void FramelessWindow::mouseDoubleClickEvent(QMouseEvent *event)
{
    QWidget* clickedWidget = QApplication::widgetAt(event->globalPos());
    // qDebug() << "clickedWidget: " << clickedWidget << ", " << event->globalPos();
    if ( clickedWidget ) {
        bool inTitle = false;
        foreach (QWidget* widget, titleBarWidgets) {
            if ( clickedWidget == widget ) {
                inTitle = true;
                break;
            }
        }

        if ( inTitle ) {
            this->setWindowState(isMaximized() ? Qt::WindowNoState : Qt::WindowMaximized);
        }
    }
}
